package odie

import (
	"bytes"
	"fmt"
	"log"
	"net/url"
	"html/template"
  "strconv"
	"code.google.com/p/feedgraph"
  "code.google.com/p/go-uuid/uuid"
)

type AppInfo struct {
	Name string
	Author string
}

type Context struct {
	url.Values
}

type Request struct {
	Session string
	Query string
	Context Context
}

type response struct {
	Query string
	Context Context
	Suggestion string
	Content string
	HTML string
	Name string
	Width int
	Height int
  AppID string
  AppInfo *AppInfo
}

type ResponseWriter struct {
	session string
  appid string
  info *AppInfo
}

func (c Context) String(name, defaultval string) (val string) {
  val = c.Get(name)
  
  if val == "" {
    val = defaultval
  }
  
  return
}

func (c Context) Integer(name string, defaultval int) (val int) {
 
  s := c.Get(name)
  
  if s == "" {
    val = defaultval
    return
  }
  
  i, err := strconv.Atoi(s)
    
  if err != nil {
    val = defaultval
    return
  }
    
  val = i
  return
}

func (w *ResponseWriter) send (r response) {

  r.AppID = w.appid
  r.AppInfo = w.info
  
	feedgraph.Publish(w.session, r)
}

// write plain-text response
func (w *ResponseWriter) Write(bs []byte) (int, error) {

	var r response
	r.Content = string(bs)

	var buf bytes.Buffer

	content.Execute(&buf, template.HTML(r.Content))

	r.HTML = buf.String()

	w.send(r)

	return len(bs), nil
}

// store client-side context variable
func (w *ResponseWriter) AddContext(name, value string) {

	var r response

	r.Context = Context{make(url.Values)}

	r.Context.Add(name, value)

	w.send(r)
}

// ask client to enter a value
func (w *ResponseWriter) Prompt(name string) {

	var r response

	r.Name = name

	var buf bytes.Buffer

	err := prompt.Execute(&buf, r)

	if err != nil {
		panic(err)
	}

	r.HTML = buf.String()

	w.send(r)
}

// suggest a follow-up query
func (w *ResponseWriter) Suggest(text string) {

	var r response

	var buf bytes.Buffer

	r.Suggestion = text

	suggest.Execute(&buf, r)

	r.HTML = buf.String()

	w.send(r)
}

// send an image to the client
func (w *ResponseWriter) Image(src string, width, height int) {

	var r response
	var buf bytes.Buffer

	r.Content = src
	r.Width = width
	r.Height = height

	image.Execute(&buf, r)

	r.HTML = buf.String()

	w.send(r)
}

// subscribe to queries from clients.  Handlers are called for matching queries.
//
// There is no port to listen on because we use long-polling and reverse-HTTP.
func (m *Matcher) SubscribeAndServe(info *AppInfo) error {

  appid := uuid.New()
  
  log.Println("AppID:", appid)
  
	helpHandler := func (w *ResponseWriter, req *Request, vars Context) {

		fmt.Fprintf(w, "<b>%s</b> was written by <i>%s</i>", info.Name, info.Author)

		for p := range m.Patterns {

			fmt.Fprintf(w, "say: '%s'", p)
		}
	}

	if info != nil {
		m.Add("help", helpHandler)
	}

	iter, err := feedgraph.Subscribe("/odie/firehose")

	if err != nil {
		return err
	}

	var req Request

	for !m.done && iter.Next(&req) {

		f, vals, err := m.Match(req.Query)

		if err != nil {
			log.Println(err, req.Query)
			continue
		}

		w := ResponseWriter{req.Session, appid, info}

		f(&w, &req, Context{vals})
    
    req = Request{}
	}

	return fmt.Errorf("Iterator closed")
}

// attempt to unsubscribe
func (m *Matcher) Cancel() {

	m.done = true
}

var DefaultMatcher = NewMatcher()



// Add a pattern to the DefaultMatcher
func Handle(pat string, handler HandlerFunc) {

	log.Println("handling", pat)
	DefaultMatcher.Add(pat, handler)
}

// SubscribeAndServe with the DefaultMatcher
func SubscribeAndServe(info *AppInfo) {

	panic(DefaultMatcher.SubscribeAndServe(info))
}

const contentTmpl = `
	<div class=response>{{.}}</div>
`

var content = template.Must(template.New("content").Parse(contentTmpl))

const suggestTmpl = `
	<span class=suggestion><button onclick='odieSubmitSuggestion("{{.Suggestion}}")'>{{.Suggestion}}</button></span>
`

var suggest = template.Must(template.New("suggest").Parse(suggestTmpl))

const promptTmpl = `
	<span class=prompt><label>{{.Content}}</label><input onchange='odieSubmitPrompt("{{.Name}}")' id="{{.Name}}" placeholder="{{.Name}}"></input></span>
`

var prompt = template.Must(template.New("prompt").Parse(promptTmpl))


const imageTmpl = `
	<span class=response><img width="{{.Width}}px" height="{{.Height}}" src="{{.Content}}"></span>
`

var image = template.Must(template.New("image").Parse(imageTmpl))
